#include "d3d9/d3d9shader.h"
#include "d3d9/d3d9display.h"

#include <stdio.h>

using namespace Kraw;

////////////////////////////////////////////////////////////////////////////////
///	D3D9 Shader

D3D9Shader::D3D9Shader() :
	pVertexShader(0),
	pPixelShader(0),
	pConstantTable(0)
{
}

D3D9Shader::~D3D9Shader()
{
	if (pConstantTable)
		pConstantTable->Release();

	if (pVertexShader)
		pVertexShader->Release();
}

// load a vertex shader from a file
D3D9Shader* D3D9Display::LoadVertexShader( const char* filename, const char* function )
{
	//TODO- if same shader already exists, AddRef it and return a clone

	//compile shader from source code:
	//requirements: pSrcData or pSrcFile (HLSL code)
	//pInclude to use packaged libraries or mangled paths
	//pDefines- to define constants from code or config files
	//pFunctionName, pProfile: what to compile from the input file
	//that might contain a lot of stuff
	//Flags:
	//return values:
	//Constant table- to access values in the shader
	//Error msgs- to log loading errors
	//Shader- the shader itself to use in the next step
/*	D3DXCompileShader( LPCSTR pSrcData,
						UINT srcDataLen,
						CONST D3DXMACRO* pDefines,
						LPD3DXINCLUDE pInclude,
						LPCSTR pFunctionName,
						LPCSTR pProfile,
						DWORD Flags,
						LPD3DXBUFFER* ppShader,
						LPD3DXBUFFER* ppErrorMsgs,
						LPD3DXCONSTANTTABLE * ppConstantTable );
	D3DXCompileShaderFromResource( HMODULE hSrcModule,
						LPCSTR pSrcResource,
						CONST D3DXMACRO* pDefines,
						... );*/

	//create the shader
	D3D9Shader* pShader = new D3D9Shader;

	//compile shader from HLSL code
	HRESULT hr;
	//TODO- shader profile should be specified in metainfo for the shader
	LPCSTR pProfile = D3DXGetVertexShaderProfile( pD3D9Device );
	//TODO- use relevant options- there are many options that skip validations once the
	//shader code is known to work, optimizations for deployment,
	DWORD compileFlags = 0;
	//compileFlags |= D3DXSHADER_DEBUG;

	LPD3DXBUFFER pShaderBuffer = 0;
	LPD3DXBUFFER pErrorBuffer = 0;
	hr = D3DXCompileShaderFromFile( filename, NULL, NULL, function, pProfile, compileFlags,
									&pShaderBuffer, &pErrorBuffer, &pShader->pConstantTable );
	if ( FAILED(hr) )
	{
        char buf[1024];
        sprintf_s( buf, "D3D9Display::LoadVertexShader() Shader failed to compile: %s\n", 
            (pErrorBuffer ? pErrorBuffer->GetBufferPointer() : "No error message") );
        Log::Write(buf);
		delete pShader;
		return 0;
	}

	//create shader from compiled code
	CONST DWORD * pFunction = (CONST DWORD *) pShaderBuffer->GetBufferPointer();
	hr = pD3D9Device->CreateVertexShader( pFunction, &pShader->pVertexShader );
	if ( SUCCEEDED(hr) )
	{
		return pShader;
	}
	
	delete pShader;
	return 0;
}

// set active shader
void D3D9Display::SetShader( D3D9Shader* pShader )
{
	ASSERT(pShader);
	if (pCurShader != pShader)
	{
		pCurShader = pShader;
		if (pShader->pVertexShader)
		{
			pD3D9Device->SetVertexShader( pShader->pVertexShader );
		}
		if (pShader->pPixelShader)
		{
			pD3D9Device->SetPixelShader( pShader->pPixelShader );
		}
		//TODO- effect files, etc.
	}
}

// set parameter alias for a system transform
void D3D9Display::SetShaderTransform( const char* name, const matrix44& mat )
{
	ASSERT(pCurShader);
	ASSERT(pCurShader->pConstantTable);

    //TODO- optimize for this (see Humus framework)
	D3DXHANDLE hConst = pCurShader->pConstantTable->GetConstantByName( NULL, name );
	if (hConst)
    {
	    pCurShader->pConstantTable->SetMatrix( pD3D9Device, hConst, (const D3DXMATRIX *) &mat );
    }
}
